using System;
using System.Collections.Generic;
using System.Text;
using System.Data;

using Deepcode.Flux.Core.Data;
using Deepcode.Flux.Core.Systems.Security;
using Deepcode.Flux.Core.Services.Security;
using Deepcode.Flux.Core.Systems.CMS.TemplateGallery;
using Deepcode.Flux.Core.Systems.CMS.DataTaxonomy;

namespace Deepcode.Flux.Core.Systems.CMS
{
	/// <summary>
	/// Represents the data for a content object on the system
	/// </summary>
	public class ContentObject
	{
		#region Protected Field Storage
		// These fields are loaded from the content record
		protected int			_PK_ID					= -1;
		protected int			_FK_ParentID			= -1;
		protected int			_Rank					= 1;
		protected string		_FK_STypeID				= "";
		protected string		_NodeTitle				= "";
		protected string		_NodeName				= "";
		protected string		_NodePath				= "";
		protected DateTime		_CreatedDT				= System.DateTime.Now;
		protected int			_FK_CreatedBy			= -1;
		protected DateTime		_LastModifiedDT			= System.DateTime.Now;
		protected int			_FK_LastModifiedBy		= -1;
		protected ContentWorkflowMode
								_WorkflowModeID			= ContentWorkflowMode.INHERIT;
		protected ContentObjectStatus 
								_WorkflowStatus			= ContentObjectStatus.COMPLETE;
		protected int			_FK_CheckedOutTo		= -1;
		protected DateTime		_CheckedOutDT			= System.DateTime.Now;
		protected int			_CurrentVersionNumber	= 1;
		protected bool			_HasEmbargoStart		= false;
		protected DateTime		_PublishStartDT			= new System.DateTime(1753, 1, 1, 0, 0, 0);
		protected bool			_HasEmbargoEnd			= false;
		protected DateTime		_PublishUntilDT			= System.DateTime.MaxValue;
		protected ContentExpiryMode
								_ExpiryActionID			= ContentExpiryMode.NOTHING;
		protected int			_ExpiryActionDataID		= -1;
		protected bool			_HasOwnSecurity			= false;

		// These fields are loaded from the version record
		protected int			_VersionNumber			= 1;
		protected int			_FK_CheckedInBy			= -1;
		protected DateTime		_CheckedInDT			= System.DateTime.Now;
		protected string		_VersionNotes			= "";
		protected string		_XMLData				= "";

		// Field holds a collection of integer ID's representing data taxonomy selections
		protected TaxonomySelections	_DataTaxonomyCategories = new TaxonomySelections();
		#endregion
		#region Exposed public properties
		/// <summary>
		/// Read only property representing the content ID
		/// </summary>
		public int PK_ID { get { return _PK_ID; } }
		/// <summary>
		/// Read only property representing the ID of the parent
		/// </summary>
		public int FK_ParentID { get { return _FK_ParentID; } }
		/// <summary>
		/// Read only - the rank of this content item
		/// </summary>
		public int Rank { get { return _Rank; } }
		/// <summary>
		/// Read only - the unique code representing the template definition of this type
		/// </summary>
		public string FK_STypeID { get { return _FK_STypeID; }}
		/// <summary>
		/// Read/Write administration title of the node
		/// </summary>
		public string NodeTitle { get { return _NodeTitle; } set { _NodeTitle = value; } }
		/// <summary>
		/// Read/Write property representing the path name of this node. Generally
		/// node names should be unique within a sibling level. Changing this property
		/// on a new record (where PK_ID is still -1) is fine, but changing it on a 
		/// record that has already been stored (PK_ID > -1) will invoke CMS.SetNodeName
		/// against the record to update any child record paths.
		/// </summary>
		public string NodeName 
		{ 
			get { return _NodeName; }
			set
			{
				if (this.PK_ID == -1) _NodeName = value;
				else
				{
					ContentManager cms = new ContentManager();
					cms.SetNodeName( this.PK_ID, value );
					_NodeName = value;
				}
			}
		}
		/// <summary>
		/// Read only path of the node - path is slash seperated list of node names
		/// up to the current location. Max length is 4000 including seperators - each
		/// node name can be 100, theoretically the maximum depth in CMS is therefore 39 
		/// levels although in reality this could be 390 (if names are kept &lt; 10 characters
		/// </summary>
		public string NodePath { get { return _NodePath; } }
		/// <summary>
		/// Read only property representing the date this item was created
		/// </summary>
		public DateTime CreatedDT { get { return _CreatedDT; } }
		/// <summary>
		/// Read only property of the user ID that created this item
		/// </summary>
		public int FK_CreatedBy { get { return _FK_CreatedBy; } }
		/// <summary>
		/// Read only property representing the date this item was last modified
		/// (modified being the last time it was published)
		/// </summary>
		public DateTime LastModifiedDT { get { return _LastModifiedDT; } }
		/// <summary>
		/// Read only property representing the ID of the user that last 
		/// modified this item of content.
		/// </summary>
		public int FK_LastModifiedBy { get { return _FK_LastModifiedBy; } }
		/// <summary>
		/// Read only property representing the mode that workflow should operate
		/// on this node. Generally this is set to inherit, but can be modified through
		/// the content manager class itself.
		/// </summary>
		public ContentWorkflowMode WorkflowModeID { get { return _WorkflowModeID; } }
		/// <summary>
		/// Read only property representing the current status of the node.
		/// </summary>
		public ContentObjectStatus WorkflowStatus { get { return _WorkflowStatus; } }
		/// <summary>
		/// Read only property representing the ID of the user this node 
		/// is checked out to. Returns -1 if no user associated.
		/// </summary>
		public int FK_CheckedOutTo { get { return _FK_CheckedOutTo; } }
		/// <summary>
		/// Read only property holding the date and time this item was checked out
		/// </summary>
		public DateTime CheckedOutDT { get { return _CheckedOutDT; } }
		/// <summary>
		/// Read only property representing the current maximum version numbver
		/// </summary>
		public int CurrentVersionNumber { get { return _CurrentVersionNumber; } }
		/// <summary>
		/// Read/Write property to determine if this content should only be published
		/// from a specific date and not before.
		/// </summary>
		public bool HasEmbargoStart{ get{ return _HasEmbargoStart; } set{ _HasEmbargoStart = value; }}
		/// <summary>
		/// Read/Write property linked to HasEmbargoStart. This is the date that the
		/// content item will be published from, IF the HasEmbargoStart property is true.
		/// </summary>
		public DateTime PublishStartDT{ get{ return _PublishStartDT; } set{ _PublishStartDT = value; }}
		/// <summary>
		/// Read/Write property to determine if this content should only be published
		/// until a certain date and not beyond.
		/// </summary>
		public bool HasEmbargoEnd{ get{ return _HasEmbargoEnd; } set{ _HasEmbargoEnd = value; }}
		/// <summary>
		/// Read/Write property linked to HasEmbargoEnd. This is the date that the
		/// content item will stop being published IF the HasEmbargoEnd property is true.
		/// </summary>
		public DateTime PublishUntilDT{ get{ return _PublishUntilDT; } set{ _PublishUntilDT = value; }}
		/// <summary>
		/// Read/Write property to determine what action to take if HasEmbargoEnd is true
		/// and the PublishUntilDT date/time has elapsed. Content can be left alone,
		/// automatically deleted or moved to an archive node (and it's embargo dates cleared)
		/// </summary>
		public ContentExpiryMode ExpiryActionID{ get{ return _ExpiryActionID; } set{ _ExpiryActionID = value; }}
		/// <summary>
		/// If HasEmbargoEnd is true and the ExpiryActionID is MOVETONODE, this read/write property
		/// holds the ID of the parent node to move the content to. If this ID does not exist when
		/// the expiration condition is true, the content will be left as expired but the action replaced
		/// with an action of NOTHING.
		/// </summary>
		public int ExpiryActionDataID{ get{ return _ExpiryActionDataID; } set{ _ExpiryActionDataID = value; }}
		/// <summary>
		/// Read only property to determine if this content item has it's own
		/// security defined or if it should inherit from the parent node.
		/// </summary>
		public bool HasOwnSecurity { get { return _HasOwnSecurity; } }
		/// <summary>
		/// Read only property representing the version number of the data held
		/// </summary>
		public int VersionNumber { get { return _VersionNumber; } }
		/// <summary>
		/// Read only property representing the version notes if any
		/// </summary>
		public string VersionNotes { get { return _VersionNotes; } }
		/// <summary>
		/// Read only property representing the version notes with 
		/// line breaks formatted as HTML BR tags.
		/// </summary>
		public string VersionNotesHTML { get { return _VersionNotes.Replace("\n", "<br/>"); } }
		/// <summary>
		/// Read only property holding the ID of the user that checked 
		/// this version of the data in or -1 if no checkin
		/// </summary>
		public int FK_CheckedInBy { get { return _FK_CheckedInBy; } }
		/// <summary>
		/// Read only date and time of the last check in if appropriate.
		/// </summary>
		public DateTime CheckedInDT { get { return _CheckedInDT; } }
		/// <summary>
		/// Readonly string holding the XML Data representing the content
		/// </summary>
		public string XMLData { get { return _fields.Serialise(); } }
		/// <summary>
		/// Represents a list of data taxonomy node ID's associated with this object
		/// </summary>
		public TaxonomySelections DataTaxonomyCategories { get { return _DataTaxonomyCategories; } }
		#endregion
		#region Fields collection representing the XML Data packet
		// Protected storage of the object field data
		protected ContentObjectData _fields = new ContentObjectData();
		/// <summary>
		/// Access the fields collection representing the various keys
		/// and values within this object.
		/// </summary>
		public ContentObjectData Fields { get { return _fields; } }

		#endregion
		#region Calculated Properties and Methods
		/// <summary>
		/// Returns the node title truncated to 40 characters
		/// </summary>
		public string NodeTitleTruncated
		{
			get
			{
				int MAXLENGTH = 40;
				if( this.NodeTitle.Length < MAXLENGTH ) return this.NodeTitle;
				return this.NodeTitle.Substring(0, MAXLENGTH - 4) + " ...";
			}
		}
		/// <summary>
		/// Determines the enhanced workflow status of this object
		/// in relation to the current user. Rather than just returning
		/// that an item is checked out, this will return that it is 
		/// checked out to the user or checked out to someone else etc.
		/// </summary>
		/// <param name="CurrentUser"></param>
		/// <returns></returns>
		public ContentObjectEnhancedStatus EnhancedStatus(AdministrationSecuritySession CurrentUser)
		{
			switch (this.WorkflowStatus)
			{
				case ContentObjectStatus.CHECKEDOUT:
					if( FK_CheckedOutTo == CurrentUser.PK_ID ) 
						return ContentObjectEnhancedStatus.CHECKEDOUTME;
					return ContentObjectEnhancedStatus.CHECKEDOUT;

				case ContentObjectStatus.CHECKEDINAWAITING:
					if( FK_CheckedInBy == CurrentUser.PK_ID ) 
							return ContentObjectEnhancedStatus.CHECKEDINAWAITINGME;
					return ContentObjectEnhancedStatus.CHECKEDINAWAITING;
				
				case ContentObjectStatus.SUBMITTED:
					return ContentObjectEnhancedStatus.SUBMITTED;

				case ContentObjectStatus.COMPLETE:
					return ContentObjectEnhancedStatus.COMPLETE;
			}
			return ContentObjectEnhancedStatus.COMPLETE;
		}
		/// <summary>
		/// Works out the descriptive status text for this item
		/// </summary>
		/// <param name="stat"></param>
		/// <returns></returns>
		public string EnhancedStatusText(AdministrationSecuritySession CurrentUser)
		{
			switch (EnhancedStatus(CurrentUser))
			{
				case ContentObjectEnhancedStatus.CHECKEDOUTME:
					return "<span class=\"g\">Checked out to <b>you</b></span>";

				case ContentObjectEnhancedStatus.CHECKEDOUT:
					if (this.CheckedOutTo != null)
						return "<span class=\"r\">Checked out to <b>" + CheckedOutTo.RealName + "(" + CheckedOutTo.EMailAddress + ")</b></span>";
					return "<span class=\"r\">Checked out to someone else</span>";

				case ContentObjectEnhancedStatus.CHECKEDINAWAITINGME:
					return "<span class=\"b\">Checked in by <b>you</b>, not yet submitted for approval</span>";

				case ContentObjectEnhancedStatus.CHECKEDINAWAITING:
					if (this.CheckedInBy != null)
						return "<span class=\"b\">Checked in by <b>" + CheckedInBy.RealName
							+ "(" + CheckedInBy.EMailAddress
							+ ")</b>, not yet submitted for approval</span>";

					return "<span class=\"b\">Checked in by someone else, not yet submitted for approval</span>";

				case ContentObjectEnhancedStatus.SUBMITTED:
					return "<span class=\"b\">In workflow process, awaiting approval</span>";
				
				case ContentObjectEnhancedStatus.COMPLETE:
					return "<span class=\"b\">Complete, Approved and Published</span>";
			}
			return "N/A";
		}
		/// <summary>
		/// Determines the embargo status of this object
		/// </summary>
		public ContentObjectEmbargoStatus EmbargoStatus
		{
			get
			{
				if (this.HasEmbargoStart)
					if( this.PublishStartDT > System.DateTime.Now ) return ContentObjectEmbargoStatus.Pending;

				if (this.HasEmbargoEnd)
				{
					if (this.PublishUntilDT > System.DateTime.Now) return ContentObjectEmbargoStatus.PublishedWillExpire;
					return ContentObjectEmbargoStatus.Expired;
				}

				return ContentObjectEmbargoStatus.PublishedPermanent;
			}
		}
		/// <summary>
		/// Returns text representing the embargo status of this object
		/// </summary>
		public string EnhancedEmbargoStatus
		{
			get
			{
				switch (EmbargoStatus)
				{
					case ContentObjectEmbargoStatus.Pending:
						return String.Format("Pending ({0})", this.PublishStartDT.ToString());
					case ContentObjectEmbargoStatus.PublishedPermanent:
						return "Published";
					case ContentObjectEmbargoStatus.PublishedWillExpire:
						return String.Format("Published until ({0})", this.PublishUntilDT.ToString());
					case ContentObjectEmbargoStatus.Expired:
						return "Expired";
				}

				return "Published";
			}
		}
		#endregion
		#region Link up properties
		/// <summary>
		/// Gets an array of content objects representing
		/// every version of this particular content object.
		/// </summary>
		public ContentObject[] AllVersions
		{
			get
			{
				ContentManager cms = new ContentManager();
				return cms.AdxGetAllVersionsFor( this.PK_ID );
			}
		}
		/// <summary>
		/// Gets the content object that is the parent of this object,
		/// and gets the current versioned content as opposed to the current
		/// published content - Use ParentLive to get the current published
		/// content.
		/// </summary>
		public ContentObject ParentStaging
		{
			get
			{
				if( this.FK_ParentID == -1 ) return null;
				ContentManager cms = new ContentManager();
				return cms.AdxGetByID( this.FK_ParentID );
			}
		}
		/// <summary>
		/// Gets the content object that is the parent of this object,
		/// and gets the current published content. This could become the
		/// current versioned content if a session variable exists called 
		/// StagingServer and is bool, set to true.
		/// </summary>
		public ContentObject ParentLive
		{
			get
			{
				if( this.FK_ParentID == -1 ) return null;
				return ContentQuery.GetByID( this.FK_ParentID );
			}
		}
		/// <summary>
		/// Gets an array of content objects that are children of this
		/// content object. Gets current content version, ignoring
		/// embargo dates and returns items in rank order.
		/// </summary>
		public ContentObject[] ChildrenStaging
		{
			get
			{
				ContentManager cms = new ContentManager(true, false);
				return cms.AdxGetByParentID( this.PK_ID, ContentObjectSortOrder.RANK, DataSortDirection.ASCENDING );
			}
		}
		/// <summary>
		/// Gets an array of content objects that are children of this content object.
		/// This version returns published information that is within the
		/// embargo date ranges. This behaviour is affected by session variables
		/// however - setting StagingServer to true will return unpublished/unapproved and
		/// latest content and setting StagingServerIgnoreDates to true will return
		/// items regardless of embargo dates.
		/// </summary>
		public ContentObject[] ChildrenLive
		{
			get
			{
				return ContentQuery.GetForParentID( this.PK_ID );
			}
		}
		/// <summary>
		/// Returns a ContentTypeTemplate object
		/// </summary>
		public ContentType TypeTemplate
		{
			get
			{
				return ContentTypes.Types.GetByTypeID( this.FK_STypeID );
			}
		}
		/// <summary>
		/// Returns the administration user that this content
		/// was originally created by. If the user cannot
		/// be found, returns null.
		/// </summary>
		public AdministrationUser CreatedBy
		{
			get
			{
				AdministrationUsers u = new AdministrationUsers();
				return u.GetUserByID( this.FK_CreatedBy );
			}
		}
		/// <summary>
		/// Returns the administration user that this content
		/// was last modified by (published by).
		/// </summary>
		public AdministrationUser LastModifiedBy
		{
			get
			{
				AdministrationUsers u = new AdministrationUsers();
				return u.GetUserByID( this.FK_LastModifiedBy );
			}
		}
		/// <summary>
		/// Returns the administration user that this content
		/// is checked out to. If not checked out or cannot
		/// be found, returns null.
		/// </summary>
		public AdministrationUser CheckedOutTo
		{
			get
			{
				AdministrationUsers u = new AdministrationUsers();
				return u.GetUserByID( this.FK_CheckedOutTo );
			}
		}
		/// <summary>
		/// Returns the administration user that this version was
		/// checked in by. If not checked in or cannot be found, returns null
		/// </summary>
		public AdministrationUser CheckedInBy
		{
			get
			{
				AdministrationUsers u = new AdministrationUsers();
				return u.GetUserByID( this.FK_CheckedInBy );
			}
		}
		/// <summary>
		/// Returns the name of the user represented by CheckedInBy
		/// However if the user no longer exists, returns text
		/// representing this fact
		/// </summary>
		public string CheckedInUsername
		{
			get
			{
				AdministrationUser u = CheckedInBy;
				if( u == null ) return "N/A";
				return u.RealName;
			}
		}
		#endregion
		#region Constructors
		/// <summary>
		/// Constructs a new content object using the type specified,
		/// in the parent specified and checked out to the owning user ID
		/// specified. (Does not save at this point, just populates the
		/// appropriate fields)
		/// </summary>
		/// <param name="TypeCode"></param>
		/// <param name="ParentID"></param>
		/// <param name="OwningUserID"></param>
		public ContentObject(string TypeCode, int ParentID, int OwningUserID)
		{
			_FK_STypeID				= TypeCode;
			_FK_ParentID			= ParentID;
			_WorkflowStatus			= ContentObjectStatus.CHECKEDOUT;
			_FK_CheckedOutTo		= OwningUserID;
			_CheckedOutDT			= System.DateTime.Now;
			_VersionNumber			= 1;
			_CurrentVersionNumber	= 1;

			// Work out the path
			RecalculatePath();
		}
		/// <summary>
		/// Constructs a new content object copying its data from the 
		/// copy source provided.
		/// </summary>
		/// <param name="copy"></param>
		public ContentObject(ContentObject copy, int ParentID, int OwnerID)
		{
			_PK_ID = -1;
			_FK_ParentID = ParentID;
			_FK_STypeID = copy.FK_STypeID;
			_NodeTitle = copy.NodeTitle;
			_NodeName = copy.NodeName;
			_NodePath = "";
			_CreatedDT = System.DateTime.Now;
			_FK_CreatedBy = OwnerID;
			_LastModifiedDT = System.DateTime.Now;
			_WorkflowModeID = copy.WorkflowModeID;
			_WorkflowStatus = ContentObjectStatus.CHECKEDOUT;
			_FK_CheckedOutTo = OwnerID;
			_CheckedOutDT = System.DateTime.Now;
			_CurrentVersionNumber = 1;
			_HasEmbargoStart = copy.HasEmbargoStart;
			_PublishStartDT = copy.PublishStartDT;
			_HasEmbargoEnd = copy.HasEmbargoEnd;
			_PublishUntilDT = copy.PublishUntilDT;
			_ExpiryActionID = copy.ExpiryActionID;
			_ExpiryActionDataID = copy.ExpiryActionDataID;
			_HasOwnSecurity = copy.HasOwnSecurity;
			_VersionNumber = 1;
			_FK_CheckedInBy = OwnerID;
			_CheckedInDT = System.DateTime.Now;
			_VersionNotes = "";
			_XMLData = copy.XMLData;
			
			// Load the XML Data into the fields collection
			_fields = new ContentObjectData();
			_fields.DeSerialise( _XMLData );

			// Load the data taxonomy information
			DataTaxonomy.DataTaxonomyTree tree = new Deepcode.Flux.Core.Systems.CMS.DataTaxonomy.DataTaxonomyTree();
			this._DataTaxonomyCategories = tree.GetForContent( copy.PK_ID );


		}
		/// <summary>
		/// Loads a content object from the datarow passed in
		/// </summary>
		/// <param name="r"></param>
		public ContentObject(DataRow r)
		{
			LoadFromRow(r);
		}
		#endregion
		#region Data loader
		/// <summary>
		/// Reloads the data based on the row passed in
		/// </summary>
		/// <param name="r"></param>
		public void LoadFromRow(DataRow r)
		{
			_PK_ID = SQLUtility.DBNull(r["PK_ID"], -1);
			_FK_ParentID = SQLUtility.DBNull(r["FK_ParentID"], -1);
			_Rank = SQLUtility.DBNull(r["Rank"], 1);
			_FK_STypeID = SQLUtility.DBNull(r["FK_STypeID"], "");
			_NodeTitle = SQLUtility.DBNull(r["NodeTitle"], "");
			_NodeName = SQLUtility.DBNull(r["NodeName"], "");
			_NodePath = SQLUtility.DBNull(r["NodePath"], "");
			_CreatedDT = SQLUtility.DBNull(r["CreatedDT"], System.DateTime.Now);
			_FK_CreatedBy = SQLUtility.DBNull(r["FK_CreatedBy"], -1);
			_LastModifiedDT = SQLUtility.DBNull(r["LastModifiedDT"], System.DateTime.Now);
			_FK_LastModifiedBy = SQLUtility.DBNull(r["FK_LastModifiedBy"], -1);
			_WorkflowModeID = (ContentWorkflowMode) SQLUtility.DBNull(r["WorkflowModeID"], ((int) ContentWorkflowMode.INHERIT));
			_WorkflowStatus = (ContentObjectStatus)SQLUtility.DBNull(r["WorkflowStatus"], ((int)ContentObjectStatus.COMPLETE));
			_FK_CheckedOutTo = SQLUtility.DBNull(r["FK_CheckedOutTo"], -1);
			_CheckedOutDT = SQLUtility.DBNull(r["CheckedOutDT"], System.DateTime.Now);
			_CurrentVersionNumber = SQLUtility.DBNull(r["CurrentVersionNumber"], 1);
			_HasEmbargoStart = SQLUtility.DBNull(r["HasEmbargoStart"], false);
			_PublishStartDT = SQLUtility.DBNull(r["PublishStartDT"], new System.DateTime(1753, 1, 1, 0, 0, 0));
			_HasEmbargoEnd = SQLUtility.DBNull(r["HasEmbargoEnd"], false);
			_PublishUntilDT = SQLUtility.DBNull(r["PublishUntilDT"], System.DateTime.MaxValue);
			_ExpiryActionID = (ContentExpiryMode) SQLUtility.DBNull(r["ExpiryActionID"], ((int) ContentExpiryMode.NOTHING));
			_ExpiryActionDataID = SQLUtility.DBNull(r["ExpiryActionDataID"], -1);
			_HasOwnSecurity = SQLUtility.DBNull(r["HasOwnSecurity"], false);
			_VersionNumber = SQLUtility.DBNull(r["VersionNumber"], 1);
			_FK_CheckedInBy = SQLUtility.DBNull(r["FK_CheckedInBy"], -1);
			_CheckedInDT = SQLUtility.DBNull(r["CheckedInDT"], System.DateTime.Now);
			_VersionNotes = SQLUtility.DBNull(r["VersionNotes"], "");
			_XMLData = SQLUtility.DBNull(r["XMLData"], "");
			
			// Load the XML Data into the fields collection
			_fields = new ContentObjectData();
			_fields.DeSerialise( _XMLData );

			// Load the data taxonomy information
			DataTaxonomy.DataTaxonomyTree tree = new Deepcode.Flux.Core.Systems.CMS.DataTaxonomy.DataTaxonomyTree();
			this._DataTaxonomyCategories = tree.GetForContent( this.PK_ID );
		}
		#endregion
		#region Utility
		/// <summary>
		/// Sets the node path to the recalculated value of it's parent - 
		/// this is only used during construction of a new content object
		/// and internally by the engine when Save is invoked on an as yet
		/// unsaved content object.
		/// </summary>
		public void RecalculatePath()
		{
			this._NodePath = "";
			ContentManager cms = new ContentManager();
			ContentObject o = cms.AdxGetByID(this.FK_ParentID);
			if (o != null)
				this._NodePath = o.NodePath + o.NodeName + "/";
			else
				this._NodePath = "/";
		}
		#endregion
	}
}
